;(function(global, factory) {
  if (typeof module === "object" && typeof module.exports === "object") {
    module.exports = global.document
      ? factory(global)
      : function(w) {
          if (!w.document) {
            throw new Error("initNECaptchaWithFallback requires a window with a document")
          }
          return factory(w)
        }
    // eslint-disable-next-line no-undef
  } else if (typeof define === "function" && define.amd) {
    // eslint-disable-next-line no-undef
    define("initNECaptchaWithFallback", [], function() {
      return factory(global)
    })
  } else {
    global.initNECaptchaWithFallback = factory(global)
  }
})(typeof window !== "undefined" ? window : this, function(window) {
  var errorCallbackCount = 0

  // 常量
  var DEFAULT_VALIDATE =
    "QjGAuvoHrcpuxlbw7cp4WnIbbjzG4rtSlpc7EDovNHQS._ujzPZpeCInSxIT4WunuDDh8dRZYF2GbBGWyHlC6q5uEi9x-TXT9j7J705vSsBXyTar7aqFYyUltKYJ7f4Y2TXm_1Mn6HFkb4M7URQ_rWtpxQ5D6hCgNJYC0HpRE7.2sttqYKLoi7yP1KHzK-PptdHHkVwb77cwS2EJW7Mj_PsOtnPBubTmTZLpnRECJR99dWTVC11xYG0sx8dJNLUxUFxEyzTfX4nSmQz_T5sXATRKHtVAz7nmV0De5unmflfAlUwMGKlCT1khBtewlgN5nHvyxeD8Z1_fPVzi9oznl-sbegj6lKfCWezmLcwft8.4yaVh6SlzXJq-FnSK.euq9OBd5jYc82ge2_hEca1fGU--SkPRzgwkzew4O4qjdS2utdPwFONnhKAIMJRPUmCV4lPHG1OeRDvyNV8sCnuFMw7leasxIhPoycl4pm5bNy70Z1laozEGJgItVNr3" // 默认validate
  var FALLBACK_LANG = {
    "zh-CN": "前方拥堵，已自动跳过验证",
    en: "captcha error，Verified automatically"
  }
  var CACHE_MIN = 1000 * 60 // 缓存时长单位，1分钟

  // 工具函数
  function loadScript(src, cb) {
    var head = document.head || document.getElementsByTagName("head")[0]
    var script = document.createElement("script")

    cb = cb || function() {}

    script.type = "text/javascript"
    script.charset = "utf8"
    script.async = true
    script.src = src

    if (!("onload" in script)) {
      script.onreadystatechange = function() {
        if (this.readyState !== "complete" && this.readyState !== "loaded") return
        this.onreadystatechange = null
        cb(null, script) // there is no way to catch loading errors in IE8
      }
    }

    script.onload = function() {
      this.onerror = this.onload = null
      cb(null, script)
    }
    script.onerror = function() {
      // because even IE9 works not like others
      this.onerror = this.onload = null
      cb(new Error("Failed to load " + this.src), script)
    }

    head.appendChild(script)
  }

  function joinUrl(protocol, host, path) {
    protocol = protocol || ""
    host = host || ""
    path = path || ""
    if (protocol) {
      protocol = protocol.replace(/:?\/{0,2}$/, "://")
    }
    if (host) {
      var matched = host.match(/^([-0-9a-zA-Z.:]*)(\/.*)?/)
      host = matched[1]
      path = (matched[2] || "") + "/" + path
    }
    !host && (protocol = "")

    return protocol + host + path
  }

  function setDomText(el, value) {
    if (value === undefined) return
    var nodeType = el.nodeType
    if (nodeType === 1 || nodeType === 11 || nodeType === 9) {
      if (typeof el.textContent === "string") {
        el.textContent = value
      } else {
        el.innerText = value
      }
    }
  }

  function queryAllByClassName(selector, node) {
    node = node || document
    if (node.querySelectorAll) {
      return node.querySelectorAll(selector)
    }
    if (!/^\.[^.]+$/.test(selector)) return []
    if (node.getElementsByClassName) {
      return node.getElementsByClassName(selector)
    }

    var children = node.getElementsByTagName("*")
    var current
    var result = []
    var className = selector.slice(1)
    for (var i = 0, l = children.length; i < l; i++) {
      current = children[i]
      if (~(" " + current.className + " ").indexOf(" " + className + " ")) {
        result.push(current)
      }
    }
    return result
  }

  function assert(condition, msg) {
    if (!condition) throw new Error("[NECaptcha] " + msg)
  }

  function isInteger(val) {
    if (Number.isInteger) {
      return Number.isInteger(val)
    }
    return typeof val === "number" && isFinite(val) && Math.floor(val) === val
  }

  function isArray(val) {
    if (Array.isArray) return Array.isArray(val)
    return Object.prototype.toString.call(val) === "[object Array]"
  }

  function ObjectAssign() {
    if (Object.assign) {
      return Object.assign.apply(null, arguments)
    }

    var target = {}
    for (var index = 1; index < arguments.length; index++) {
      var source = arguments[index]
      if (source != null) {
        for (var key in source) {
          if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key]
          }
        }
      }
    }
    return target
  }

  function getTimestamp(msec) {
    msec = !msec && msec !== 0 ? msec : 1
    return parseInt(new Date().valueOf() / msec, 10)
  }

  // 降级方案
  function normalizeFallbackConfig(customConfig) {
    var siteProtocol = window.location.protocol.replace(":", "")
    var defaultConf = {
      protocol: siteProtocol === "http" ? "http" : "https",
      lang: "zh-CN",
      errorFallbackCount: 3
    }
    var config = ObjectAssign({}, defaultConf, customConfig)

    var errorFallbackCount = config.errorFallbackCount
    assert(
      errorFallbackCount === undefined ||
        (isInteger(errorFallbackCount) && errorFallbackCount >= 1),
      "errorFallbackCount must be an integer, and it's value greater than or equal one"
    )

    return config
  }

  function loadResource(config, cb) {
    if (window.initNECaptcha) return cb(null)
    function genUrl(server) {
      var path = "load.min.js"
      var urls = []
      if (isArray(server)) {
        for (var i = 0, len = server.length; i < len; i++) {
          urls.push(joinUrl(config.protocol, server[i], path))
        }
      } else {
        var url = joinUrl(config.protocol, server, path)
        urls = [url, url]
      }

      return urls
    }
    var urls = genUrl(config.staticServer || ["cstaticdun.126.net", "cstatic.dun.163yun.com"])

    function step(i) {
      var url = urls[i] + "?v=" + getTimestamp(CACHE_MIN)
      loadScript(url, function(err) {
        if (err || !window.initNECaptcha) {
          // loadjs的全局变量
          i = i + 1
          if (i === urls.length) {
            return cb(
              new Error(
                "Failed to load script(" + url + ")." + (err ? err.message : "unreliable script")
              )
            )
          }
          return step(i)
        }
        return cb(null)
      })
    }
    step(0)
  }

  function initNECaptchaWithFallback(options, onload, onerror) {
    var captchaIns = null

    var config = normalizeFallbackConfig(options)
    var defaultFallback = config.defaultFallback !== false
    var langPkg = FALLBACK_LANG[config.lang === "zh-CN" ? config.lang : "en"]
    var storeKey = window.location.pathname + "_" + config.captchaId + "_NECAPTCHA_ERROR_COUNTS"
    try {
      errorCallbackCount = parseInt(localStorage.getItem(storeKey) || 0, 10)
    } catch (error) {}

    var fallbackFn = !defaultFallback
      ? config.onFallback || function() {}
      : function(validate) {
          function setFallbackTip(instance) {
            if (!instance) return
            setFallbackTip(instance._captchaIns)
            if (!instance.$el) return
            var tipEles = queryAllByClassName(".yidun-fallback__tip", instance.$el)
            if (!tipEles.length) return

            // 确保在队列的最后
            setTimeout(function() {
              for (var i = 0, l = tipEles.length; i < l; i++) {
                setDomText(tipEles[i], langPkg)
              }
            }, 0)
          }
          setFallbackTip(captchaIns)

          config.onVerify && config.onVerify(null, { validate: validate })
        }
    var noFallback = !defaultFallback && !config.onFallback

    var proxyOnError = function(error) {
      errorCallbackCount++
      if (errorCallbackCount < config.errorFallbackCount) {
        try {
          localStorage.setItem(storeKey, errorCallbackCount)
        } catch (err) {}

        onerror(error)
      } else {
        fallbackFn(DEFAULT_VALIDATE)
        proxyRefresh()
        noFallback && onerror(error)
      }
    }

    var proxyRefresh = function() {
      errorCallbackCount = 0
      try {
        localStorage.setItem(storeKey, 0)
      } catch (err) {}
    }

    config.onError = proxyOnError
    config.onDidRefresh = proxyRefresh

    loadResource(config, function(error) {
      if (error) {
        proxyOnError(error)
      } else {
        window.initNECaptcha(
          config,
          function(instance) {
            captchaIns = instance
            onload && onload(instance)
          },
          noFallback
            ? onerror
            : function(error) {
                proxyOnError(error)
              }
        )
      }
    })
  }

  return initNECaptchaWithFallback
})
